package org.easyx.ui.aop;

import java.util.Date;
import java.util.Enumeration;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.easyx.core.ui.util.WebUtil;
import org.easyx.core.utils.JsonKit;
import org.easyx.core.utils.ObjKit;
import org.easyx.ser.sys.entity.Log;
import org.easyx.ser.sys.serivce.LogService;
import org.easyx.ui.base.BossEasyUISessionUser;
import org.easyx.ui.util.SessionKit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

/**
 * AOP 日志
 */
@Component
@Aspect
public class SysLogAspect {
    private Logger log = LoggerFactory.getLogger(SysLogAspect.class);
    @Autowired
    private LogService logService;
    
    //配置切入点,该方法无方法体,主要为方便同类中其他方法使用此处配置的切入点
    @SuppressWarnings("unused")
	@Pointcut("within(@org.springframework.stereotype.Controller *)")
    private void cutController() {}
    
    /**
     *  前置通知（Before advice）：在某连接点（JoinPoint）之前执行的通知，但这个通知不能阻止连接点前的执行。ApplicationContext中在<aop:aspect>里面使用<aop:before>元素进行声明。例如，TestAspect中的doBefore方法。
		后置通知（After advice）：当某连接点退出的时候执行的通知（不论是正常返回还是异常退出）。ApplicationContext中在<aop:aspect>里面使用<aop:after>元素进行声明。例如，ServiceAspect中的returnAfter方法，所以Teser中调用UserService.delete抛出异常时，returnAfter方法仍然执行。
		返回后通知（After return advice）：在某连接点正常完成后执行的通知，不包括抛出异常的情况。ApplicationContext中在<aop:aspect>里面使用<after-returning>元素进行声明。
		环绕通知（Around advice）：包围一个连接点的通知，类似Web中Servlet规范中的Filter的doFilter方法。可以在方法的调用前后完成自定义的行为，也可以选择不执行。ApplicationContext中在<aop:aspect>里面使用<aop:around>元素进行声明。例如，ServiceAspect中的around方法。
		抛出异常后通知（After throwing advice）：在方法抛出异常退出时执行的通知。ApplicationContext中在<aop:aspect>里面使用<aop:after-throwing>元素进行声明。例如，ServiceAspect中的returnThrow方法。
     */
    @Around("cutController()")
    public Object recordSysLog(ProceedingJoinPoint point) throws Throwable {
    	log.debug("系统日志Aop，业务逻辑调用结束->开始...");
    	Object ret = null;
    	String resCode = "200";
		String resText = "";
    	long timeStart = System.currentTimeMillis();
    	try {
			ret = point.proceed();
			resText = JsonKit.toJson(ret);
			
		} catch (Exception e) {
			resCode = "500";
			resText = "出现异常："+e.getMessage();
			log.error("系统日志Aop出现异常：",e);
		}
    	long timeEnd = System.currentTimeMillis();
    	long times = timeEnd-timeStart;
    	log.info("系统日志Aop，业务逻辑调用结束->耗时："+times+" ms");
    	
    	log.debug("系统日志Aop，保存日志信息，开始...");
    	doSaveLogInfo(point,resCode,resText,times);
    	log.debug("系统日志Aop，保存日志信息，结束!!!");
    	
    	return ret;
    }

	private void doSaveLogInfo(ProceedingJoinPoint point, String resCode,String resText, long times) {
		try{
    		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        	//请求URL
            String requestUrl = request.getRequestURI().replace(request.getContextPath(), "");
        	//方法名
            String strMethodName = point.getSignature().getName();
            //类名
            String strClassName = point.getTarget().getClass().getName();
            //参数
            Object[] params = point.getArgs();
            StringBuffer bfParams = new StringBuffer();
            Enumeration<String> paraNames = null;
            if (params != null && params.length > 0) {
                paraNames = request.getParameterNames();
                String key;
                String value;
                while (paraNames.hasMoreElements()) {
                    key = paraNames.nextElement();
                    value = request.getParameter(key);
                    bfParams.append(key).append("=").append(value).append("&");
                }
                if (ObjKit.isEmpty(bfParams)) {
                    bfParams.append(request.getQueryString());
                }
            }
            String loginName = "匿名用户";
        	String realName = "匿名用户";
        	long userId = -1;
        	BossEasyUISessionUser suser = SessionKit.getSessionUser(request);
        	if(suser!=null){
        		loginName = suser.getLoginName();
        		realName = suser.getRealName();
        		userId = suser.getUserId();
        	}
        	
        	String strMessage = String.format("[请求地址]:%s,[类名]:%s,[方法]:%s,[参数]:%s", requestUrl,strClassName, strMethodName, bfParams.toString());
        	Log sysLog = new Log();
            sysLog.setLoginName(loginName);
            sysLog.setRealName(realName);
            sysLog.setOptContent(strMessage);
            sysLog.setCreateTime(new Date());
            sysLog.setCreateBy(userId);
            sysLog.setClientIp(WebUtil.getIpAddress(request));
			sysLog.setResultCode(resCode);
			sysLog.setResultText(resText);
			sysLog.setRequestTime(times);
			
			saveLog2DBorNot(strMethodName, sysLog);
            
    	} catch (Exception e) {
        	log.error("系统日志AOP，保存日志信息出现异常!",e);
        }
	}

	private void saveLog2DBorNot(String strMethodName, Log sysLog) {
		log.info("系统日志AOP对象："+sysLog.toString());
		if (isWriteLog(strMethodName)) {
			//入库
	    	logService.insert(sysLog);
		}else{
			log.debug(strMethodName+"属于非关键系统日志，不写入数据库！");
		}
	}

    private boolean isWriteLog(String method) {
        String[] pattern = {"login", "logout", "add", "edit", "delete", "assign", "pause", "resume", "run"};
        for (String s : pattern) {
            if (method.indexOf(s) > -1 
            		&& !(method.indexOf("UI")>-1) 
            		&& !(method.indexOf("Page")>-1)) {
                return true;
            }
        }
        return false;
    }
    
    
    
//  //拦截所有方法
//  @Pointcut("execution(public * *(..))")
//  private void aspectMethod(){}
//  
//  /*
//	 * 配置前置通知,使用在方法aspect()上注册的切入点
//	 * 同时接受JoinPoint切入点对象,可以没有该参数
//	 */
//	@Before(value = "aspectMethod() && @annotation(org.springframework.web.bind.annotation.RequestMapping)")
//	public void before(JoinPoint joinPoint){
//		if(log.isInfoEnabled()){
//			log.info("before " + joinPoint);
//		}
//		System.out.println("111111111111111111111");
//	}
//	
//	//配置后置通知,使用在方法aspect()上注册的切入点
//	@After(value = "aspectMethod() && @annotation(org.springframework.web.bind.annotation.RequestMapping)")
//	public void after(JoinPoint joinPoint){
//		if(log.isInfoEnabled()){
//			log.info("after " + joinPoint);
//		}
//		System.out.println("22222222222222222222222222");
//	}
}
